﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Affirma.ThreeSharp.Wrapper;
using log4net;
using System.IO;
using System.Xml;

namespace CloudDrive.S3
{
    public class FileCache
    {
        private object _locker = new object();
        private IThreeSharpWrapper _ThreeSharpWrapper;
        public FileCache(IThreeSharpWrapper threeSharpWrapper, string bucketName)
        {
            _BucketName = bucketName;
            _ThreeSharpWrapper = threeSharpWrapper;
        }
        private IList<RemoteFileInfo> _Files = null;
        private string _BucketName;
        public IEnumerable<RemoteFileInfo> Files
        {
            get
            {
                lock (_locker)
                {
                    if (_Files == null)
                    {
                        string xmlResponse = _ThreeSharpWrapper.ListBucket(_BucketName);
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(xmlResponse);
                        var documentElement = xmlDoc.DocumentElement;
                        XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
                        nsManager.AddNamespace("x", documentElement.NamespaceURI);
                        var nav = documentElement.SelectNodes("/x:ListBucketResult/x:Contents", nsManager);
                        _Files = new List<RemoteFileInfo>();
                        foreach (XmlNode node in nav)
                        {
                            RemoteFileInfo info = new RemoteFileInfo()
                            {
                                FullName = "/" + node.SelectSingleNode("x:Key", nsManager).InnerText,
                                Size = long.Parse(node.SelectSingleNode("x:Size", nsManager).InnerText)
                            };
                            _Files.Add(info);
                        }
  
                    }
                }
                return _Files;
            }
        }
        public bool FileExists(string path)
        {
            foreach (var file in Files)
            {
                if (file.FullName.Equals(path, StringComparison.CurrentCultureIgnoreCase))
                    return true;
            }
            return false;
        }
        public void AddFile(RemoteFileInfo file)
        {
            var files = Files as IList<RemoteFileInfo>;
            files.Add(file);
        }
    }
    public class RemoteFileManager
    {
        private IThreeSharpWrapper _ThreeSharpWrapper;
        private ILog _Log;
        private FileCache _FileCache;
        private string _BucketName;
        public RemoteFileManager(IThreeSharpWrapper threeSharpWrapper, string bucketName, FileCache fileCache)
        {
            _BucketName = bucketName;
            _ThreeSharpWrapper = threeSharpWrapper;
            _FileCache = fileCache;
            _Log = LogManager.GetLogger(typeof(RemoteFileManager));           
        }
        private void InsureValidPath(string path)
        {
            var utf8Key = ASCIIEncoding.UTF8.GetBytes(path);
            if (utf8Key.Length > 128)
                throw new PathTooLongException();
        }
        private string CreateTempFile()
        {
            string tempFile = Path.GetTempFileName();
            var stream = File.Create(tempFile);
            stream.Close();
            return tempFile;
        }
        public RemoteFileInfo CreateFile(string path, FileMode mode)
        {
            InsureValidPath(path);
            if (mode == FileMode.CreateNew
                && _FileCache.FileExists(path))
                throw new IOException(path + " already exists");
            string tempFile = CreateTempFile();
            RemoteFileInfo info = new RemoteFileInfo()
            {
                FullName = path,
                Size = 0,
                LocalFile = tempFile,
                CreationDate = DateTime.Now
            };

            lock (_FileCache)
            {
                if (!_FileCache.FileExists(info.KeyName))
                {
                    _FileCache.AddFile(info);
                    _ThreeSharpWrapper.AddFileObject(_BucketName, info.KeyName, info.LocalFile);
                }
            }
            return info;
        }
    }
}
